/**
 * 
 */
package org.statewalker.fsm.providers.conf;

import java.util.HashMap;
import java.util.Map;
import java.util.Stack;

import org.statewalker.fsm.providers.FsmTransitionsConst;
import org.statewalker.fsm.providers.IFsmTransitions;
import org.statewalker.fsm.providers.IFsmTransitionsRegistry;

/**
 * @author kotelnikov
 */
public abstract class AbstractFsmTransitionsConfigurator<SK, EK>
    implements
    IFsmTransitionsConfigurator<SK, EK> {

    public static class FsmTransitions<SK, EK>
        implements
        IFsmTransitions<SK, EK> {

        private Map<EK, SK> fDefaultTransitions;

        private final SK fStateKey;

        private Map<SK, Map<EK, SK>> fTransitions;

        public FsmTransitions(SK stateKey) {
            fStateKey = stateKey;
        }

        public void addDefaultTransition(EK event, SK to) {
            if (fDefaultTransitions == null)
                fDefaultTransitions = new HashMap<EK, SK>();
            fDefaultTransitions.put(event, to);
        }

        public void addTransition(SK from, EK event, SK to) {
            if (fTransitions == null)
                fTransitions = new HashMap<SK, Map<EK, SK>>();
            Map<EK, SK> map = fTransitions.get(from);
            if (map == null) {
                map = new HashMap<EK, SK>();
                fTransitions.put(from, map);
            }
            map.put(event, to);
        }

        @Override
        public boolean equals(Object obj) {
            if (obj == this)
                return true;
            if (!(obj instanceof FsmTransitions<?, ?>))
                return false;
            FsmTransitions<?, ?> o = (FsmTransitions<?, ?>) obj;
            return toString().equals(o.toString());
        }

        public SK getDefaultTarget(EK event) {
            if (fDefaultTransitions == null)
                return null;
            SK target = fDefaultTransitions.get(event);
            return target;
        }

        /**
         * @return the stateKey
         */
        public SK getStateKey() {
            return fStateKey;
        }

        public SK getTarget(SK from, EK event) {
            if (fTransitions == null)
                return null;
            Map<EK, SK> map = fTransitions.get(from);
            if (map == null)
                return null;
            SK target = map.get(event);
            return target;
        }

        @Override
        public int hashCode() {
            int first = fDefaultTransitions != null ? fDefaultTransitions
                .hashCode() : 0;
            int second = fTransitions != null ? fTransitions.hashCode() : 0;
            return first ^ second;
        }

        @Override
        public String toString() {
            StringBuilder builder = new StringBuilder();
            builder.append("Transitions: [").append(fTransitions).append(
                "] DefaultTransitions: [").append(fDefaultTransitions).append(
                "]");
            return builder.toString();
        }

    }

    private FsmTransitionsConst<SK, EK> fInfo;

    protected Stack<IFsmTransitionsRegistry<SK, EK>> fRegistryStack = new Stack<IFsmTransitionsRegistry<SK, EK>>();

    protected Stack<FsmTransitions<SK, EK>> fTransitionsStack = new Stack<FsmTransitions<SK, EK>>();

    public AbstractFsmTransitionsConfigurator(FsmTransitionsConst<SK, EK> info) {
        fInfo = info;
    }

    /**
     * @see org.statewalker.fsm.providers.IFsmConfigurator#beginState(java.lang.Object)
     */
    public void beginState(SK state) {
        IFsmTransitionsRegistry<SK, EK> registry = getFsmTransitionsRegistry(state);
        FsmTransitions<SK, EK> transitions = (FsmTransitions<SK, EK>) registry
            .getTransitions(state);
        if (transitions == null) {
            transitions = newFsmTransitions(state);
            registry.registerTransitions(state, transitions);
        }
        fTransitionsStack.push(transitions);
        fRegistryStack.push(registry);
    }

    /**
     * @see org.statewalker.fsm.providers.IFsmConfigurator#endState()
     */
    public void endState() {
        fTransitionsStack.pop();
        fRegistryStack.pop();
    }

    protected abstract IFsmTransitionsRegistry<SK, EK> getFsmTransitionsRegistry(
        SK state);

    protected FsmTransitions<SK, EK> newFsmTransitions(SK stateKey) {
        return new FsmTransitions<SK, EK>(stateKey);
    }

    /**
     * @see org.statewalker.fsm.providers.IFsmConfigurator#onDefaultTransition(java.lang.Object,
     *      java.lang.Object)
     */
    public void onDefaultTransition(EK event, SK to) {
        FsmTransitions<SK, EK> transitions = fTransitionsStack.peek();
        if (transitions == null)
            return;
        if (to == null)
            to = fInfo.getFinalStateKey();
        transitions.addDefaultTransition(event, to);
    }

    /**
     * @see org.statewalker.fsm.providers.IFsmConfigurator#onTransition(java.lang.Object,
     *      java.lang.Object, java.lang.Object)
     */
    public void onTransition(SK from, EK event, SK to) {
        FsmTransitions<SK, EK> transitions = fTransitionsStack.peek();
        if (transitions == null)
            return;
        if (from == null)
            from = fInfo.getInitialStateKey();
        if (to == null)
            to = fInfo.getFinalStateKey();
        transitions.addTransition(from, event, to);
    }
}
